package com.rxh.trade;

import com.rxh.pojo.Result;
import com.rxh.pojo.core.CoreAcquirerAccountParam;
import com.rxh.pojo.cross.BankResult;
import com.rxh.pojo.cross.GrePayBean;
import com.rxh.pojo.payment.Refund;
import com.rxh.pojo.payment.Trade;
import com.rxh.pojo.sys.SysConstant;
import com.rxh.utils.*;
import org.apache.commons.codec.digest.DigestUtils;
import com.rxh.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class GrePay implements AcquirerInterface<GrePayBean> {
    private final static String ACCEPTED = "00";
    private final static String SUCCESS = "10";
    private final static String FAIL = "11";
    private final static Logger logger = LoggerFactory.getLogger(GrePay.class);
    private final static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");

    @Value("${GrePay.payNotifyUrl}")
    private String notifyUrl;

    @Value("${GrePay.refundNotifyUrl}")
    private String refundNotifyUrl;

    @Value("${GrePay.id}")
    private Integer acquirerId;

    // 该取值方法为解决通知无法获取MD5Key的缓兵之计！
    @Value("${GrePay.MD5Key}")
    private String md5Key;

    private CoreAcquirerAccountParam param;

    private final PaymentInfo paymentInfo;

    @Autowired
    public GrePay(PaymentInfo paymentInfo) {
        this.paymentInfo = paymentInfo;
    }

    @Override
    public Integer getAcquirerId() {
        return acquirerId;
    }

    @Override
    public BankResult trade(Trade trade) {
        param = JsonUtils.jsonToPojo(trade.getCoreAcquirerAccount().getParams(), CoreAcquirerAccountParam.class);
        String content = HttpClientUtils.doPost(HttpClientUtils.getHttpClient(), param.getPayUrl(), getTradeParam(trade));
        if (StringUtils.isNotBlank(content)) {
            return verifyResult(content);
        } else {
            return new BankResult(SystemConstant.BANK_STATUS_TIME_OUT);
        }
    }

    @Override
    public String tradeNotify(GrePayBean result) {
        String md5Str = "version=1.6" +
                "&signType=" + StringUtils.getToStringInsteadOfNull(result.getSignType()) +
                "&merchantOrderNo=" + StringUtils.getToStringInsteadOfNull(result.getMerchantOrderNo()) +
                "&currencyCode=" + StringUtils.getToStringInsteadOfNull(result.getCurrencyCode()) +
                "&orderAmount=" + StringUtils.getToStringInsteadOfNull(result.getOrderAmount()) +
                "&paidAmount=" + StringUtils.getToStringInsteadOfNull(result.getPaidAmount()) +
                "&submitTime=" + StringUtils.getToStringInsteadOfNull(result.getSubmitTime()) +
                "&resultCode=" + StringUtils.getToStringInsteadOfNull(result.getResultCode()) +
                "&key=" + md5Key;
        if (StringUtils.equals(result.getSignMsg(), DigestUtils.md5Hex(md5Str).toUpperCase())) {
            BankResult bankResult = new BankResult();
            bankResult.setOrderId(result.getMerchantOrderNo());
            bankResult.setParam(JsonUtils.objectToJson(result)); /////
            Date date = DateUtils.dateFormat(dateFormat, result.getDealTime());
            bankResult.setTime(date == null ? new Date() : date);
            switch (result.getResultCode()) {
                case SUCCESS:
                    bankResult.setStatus(SystemConstant.BANK_STATUS_SUCCESS);
                    bankResult.setBankResult("交易成功");
                    bankResult.setBankCode("pay.1047");
                    break;
                case ACCEPTED:
                    bankResult.setStatus(SystemConstant.BANK_STATUS_UNPAID);
                    bankResult.setBankResult("订单已受理");
                    bankResult.setBankCode("pay.1043");
                    break;
                case FAIL:
                    bankResult.setStatus((short) 1);
                    bankResult.setBankResult("付款失败：" + result.getErrMsg());
                    bankResult.setBankCode("error.5000");
                    break;
                default:
            }
            String msg = HttpClientUtils.doPostJson(HttpClientUtils.getHttpClient(), paymentInfo.getBankNotifyUrl(), JsonUtils.objectToJson(bankResult));
            Result paymentResult = JsonUtils.jsonToPojo(msg, Result.class);
            if (paymentResult == null) {
                return "FAIL";
            }
            if (paymentResult.getCode() == Result.SUCCESS) {
                return "OK";
            } else {
                return paymentResult.getMsg();
            }
        } else {
            logger.error("非法请求：MD5验证失败！");
            return "MD5验证失败";
        }
    }

    @Override
    public BankResult tradeQuery(Trade trade) {
        return null;
    }

    @Override
    public BankResult tradeListQuery(Trade trade) {
        return null;
    }

    @Override
    public BankResult refund(Refund refund) {
        param = JsonUtils.jsonToPojo(refund.getCoreAcquirerAccount().getParams(), CoreAcquirerAccountParam.class);
        BigDecimal refundAmount = refund.getChannelOrderInfo() == null ?
                refund.getCoreOrderChange().getRmbAmount().multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP) :
                refund.getChannelOrderInfo().getChannelAmount().multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP);
        Map<String, Object> refundParam = new HashMap<>();
        refundParam.put("version", "1.9");
        refundParam.put("charset", "UTF-8");
        refundParam.put("merchantId", param.getMerNum());// 商户号
        refundParam.put("signType", "MD5");
        refundParam.put("language", "en");
        // 业务参数
        refundParam.put("refundOrderNo", refund.getCoreOrderChange().getId());
        refundParam.put("merchantOrderNo", refund.getOrder().getId());
        refundParam.put("masapayOrderNo", refund.getOrder().getBankOrderId());
        refundParam.put("refundAmount", refundAmount);
        refundParam.put("currencyCode", "CNY");
        refundParam.put("refundSubmitTime", dateFormat.format(refund.getCoreOrderChange().getApplicationTime()));
        refundParam.put("callbackUrl", refundNotifyUrl);
        String md5Txt = "version=1.9" +
                "&merchantId=" + param.getMerNum() +
                "&charset=UTF-8" +
                "&language=en" +
                "&signType=MD5" +
                "&refundOrderNo=" + refund.getCoreOrderChange().getId() +
                "&merchantOrderNo=" + refund.getOrder().getId() +
                "&masapayOrderNo=" + refund.getOrder().getBankOrderId() +
                "&refundAmount=" + refundAmount +
                "&currencyCode=CNY" +
                "&refundSubmitTime=" + dateFormat.format(refund.getCoreOrderChange().getApplicationTime()) +
                "&callbackUrl=" + refundNotifyUrl +
                "&key=" + param.getMd5key();
        refundParam.put("signMsg", DigestUtils.md5Hex(md5Txt).toUpperCase());
        logger.info("GrePay退款申请请求参数：" + refundParam);
        String content = HttpClientUtils.doPost(HttpClientUtils.getHttpClient(), param.getRefundUrl(), refundParam);
        logger.info("GrePay退款申请返回参数：" + content);
        GrePayBean result = JsonUtils.jsonToPojo(content, GrePayBean.class);
        BankResult bankResult = new BankResult();
        // 返回MD5信息
        if (result != null) {
            String returnMd5Txt = "version=" + StringUtils.getToStringInsteadOfNull(result.getVersion()) +
                    "&merchantId=" + param.getMerNum() +
                    "&charset=" + StringUtils.getToStringInsteadOfNull(result.getCharset()) +
                    "&language=" + StringUtils.getToStringInsteadOfNull(result.getLanguage()) +
                    "&signType=" + StringUtils.getToStringInsteadOfNull(result.getSignType()) +
                    "&resultCode=" + StringUtils.getToStringInsteadOfNull(result.getResultCode()) +
                    "&refundOrderNo=" + StringUtils.getToStringInsteadOfNull(result.getRefundOrderNo()) +
                    "&key=" + param.getMd5key();
            // 签名验证
            if (StringUtils.equals(result.getSignMsg(), DigestUtils.md5Hex(returnMd5Txt).toUpperCase())) {
                bankResult.setOrderId(refund.getOrder().getId());
                bankResult.setParam(content);
                switch (result.getResultCode()) {
                    case SUCCESS:
                        bankResult.setStatus(BankResult.PROCESSING);
                        bankResult.setBankResult("退款申请成功");
                        break;
                    case FAIL:
                        bankResult.setStatus(BankResult.FAIL);
                        bankResult.setBankResult("退款申请失败：" + result.getErrorMsg());
                        break;
                    default:
                        logger.warn("未知状态：" + result.getResultCode() + "，返回信息：" + content);
                        break;
                }
            } else {
                logger.error("MD5验证失败：" + returnMd5Txt);
                bankResult.setStatus(SystemConstant.CHANGE_BANK_STATUS_FAIL);
                bankResult.setBankResult("退款申请失败：MD5验证失败");
            }
            return bankResult;
        } else {
            String msg = "退款申请：银行返回Json转换异常！";
            logger.error(msg);
            bankResult.setStatus(SystemConstant.CHANGE_BANK_STATUS_SUCCESS);
            bankResult.setBankResult(msg);
            return bankResult;
        }
    }

    @Override
    public String refundNotify(GrePayBean result) {
        String md5Txt = "version=" + StringUtils.getToStringInsteadOfNull(result.getVersion()) +
                "&charset=" + StringUtils.getToStringInsteadOfNull(result.getCharset()) +
                "&language=" + StringUtils.getToStringInsteadOfNull(result.getLanguage()) +
                "&signType=" + StringUtils.getToStringInsteadOfNull(result.getLanguage()) +
                "&refundOrderNo=" + StringUtils.getToStringInsteadOfNull(result.getRefundOrderNo()) +
                "&merchantOrderNo=" + StringUtils.getToStringInsteadOfNull(result.getMerchantOrderNo()) +
                "&grepayRefundOrderNo=" + StringUtils.getToStringInsteadOfNull(result.getGrepayRefundOrderNo()) +
                "&submitTime=" + StringUtils.getToStringInsteadOfNull(result.getSubmitTime()) +
                "&dealTime=" + StringUtils.getToStringInsteadOfNull(result.getDealTime()) +
                "&currencyCode=" + StringUtils.getToStringInsteadOfNull(result.getCurrencyCode()) +
                "&refundAmount=" + StringUtils.getToStringInsteadOfNull(result.getRefundAmount()) +
                "&resultCode=" + StringUtils.getToStringInsteadOfNull(result.getResultCode()) +
                "&key=" + md5Key;
        if (StringUtils.equals(result.getSignMsg(), DigestUtils.md5Hex(md5Txt).toUpperCase())) {
            BankResult bankResult = new BankResult();
            bankResult.setOrderId(result.getMerchantOrderNo());
            bankResult.setBankRefundId(result.getGrepayRefundOrderNo());
            bankResult.setParam(JsonUtils.objectToJsonNonNull(result));
            Date date = DateUtils.dateFormat(dateFormat, result.getDealTime());
            bankResult.setTime(date == null ? new Date() : date);
            switch (result.getResultCode()) {
                case SUCCESS:
                    bankResult.setStatus(SystemConstant.CHANGE_BANK_STATUS_SUCCESS);
                    bankResult.setBankResult("退款成功");
                    break;
                case FAIL:
                    bankResult.setStatus(SystemConstant.CHANGE_BANK_STATUS_FAIL);
                    bankResult.setBankResult("退款失败：" + result.getErrMsg());
                    break;
                default:
                    logger.warn("未知状态：" + result.getResultCode() + "，返回信息：" + JsonUtils.objectToJsonNonNull(result));
                    break;
            }
            String msg = HttpClientUtils.doPostJson(HttpClientUtils.getHttpClient(), paymentInfo.getRefundNotifyUrl(), JsonUtils.objectToJsonNonNull(result));
            Result paymentResult = JsonUtils.jsonToPojo(msg, Result.class);
            if (paymentResult == null) {
                return "FAIL";
            }
            if (paymentResult.getCode() == Result.SUCCESS) {
                return "OK";
            } else {
                return paymentResult.getMsg();
            }
        } else {
            return "MD5验证失败";
        }
    }

    @Override
    public BankResult refundQuery(Refund refund) {
        return null;
    }

    @Override
    public BankResult refundListQuery(Refund refund) {
        return null;
    }

    // 请求参数处理
    private Map<String, Object> getTradeParam(Trade trade) {
        //金额换算成为分为单位，如果金额换算成为分不是整数，四舍五入
        BigDecimal orderAmount = trade.getChannelOrderInfo().getChannelAmount().multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP);
        StringBuilder sb = new StringBuilder();
        for (int i = 0, s = trade.getProductDetails().size(); i < s; i++) {
            sb.append(trade.getProductDetails().get(i).getGoodName()).append("|");
        }
        String goodsName = sb.length() < 1000 ? sb.toString() : sb.toString().substring(0, 1000);
        String cardExpirationMonth = trade.getCoreCardDetail().getExpMonth() < 10 ? "0" + trade.getCoreCardDetail().getExpMonth() : String.valueOf(trade.getCoreCardDetail().getExpMonth());
        Map<String, Object> tradeParam = new HashMap<>();
        //基础参数
        tradeParam.put("version", "1.6");
        tradeParam.put("merchantId", param.getMerNum());//商户号
        tradeParam.put("charset", "utf-8");
        tradeParam.put("language", trade.getCoreOrderDetail().getLang());//语言
        tradeParam.put("signType", "MD5");
        //业务参数
        tradeParam.put("merchantOrderNo", trade.getCoreOrder().getId());//平台订单号
        tradeParam.put("goodsName", goodsName);
        tradeParam.put("goodsDesc", goodsName);
        tradeParam.put("orderExchange", "3");//1异步订单，2同步订单，默认同步，3同步风控订单
        tradeParam.put("currencyCode", "CNY");//币种要传入3为简称     order.getString("currency")
        tradeParam.put("orderAmount", orderAmount);
        tradeParam.put("submitTime", dateFormat.format(trade.getCoreOrder().getTradeTime()));//订单提交时间
        tradeParam.put("expiryTime", dateFormat.format(DateUtils.addDay(trade.getCoreOrder().getTradeTime(), 1)));//失效时间默认+一天
        tradeParam.put("bgUrl", notifyUrl);//通知地址
        //支付参数
        tradeParam.put("payMode", "10");//国际信用卡
        tradeParam.put("orgCode", trade.getCoreOrder().getPayType().toUpperCase());
        tradeParam.put("cardNumber", trade.getTradeObject().getCardNo());
        tradeParam.put("cardHolderFirstName", trade.getCoreCardholder().getFirstName());
        tradeParam.put("cardHolderLastName", trade.getCoreCardholder().getLastName());
        tradeParam.put("cardExpirationMonth", cardExpirationMonth);
        tradeParam.put("cardExpirationYear", trade.getTradeObject().getExpireYear());
        tradeParam.put("securityCode", trade.getTradeObject().getSecurityNum());
        tradeParam.put("cardHolderEmail", trade.getCoreCardholder().getEmail());
        tradeParam.put("cardHolderPhoneNumber", trade.getCoreCardholder().getPhone());
        //账单信息
        tradeParam.put("billName", trade.getCoreCardholder().getFirstName() + " " + trade.getCoreCardholder().getLastName());
        tradeParam.put("billAddress", trade.getCoreCardholder().getAddress());
        tradeParam.put("billPostalCode", trade.getCoreCardholder().getZip());
        tradeParam.put("billCountry", trade.getCoreCardholder().getCountry().toUpperCase());//账单国家和收获国家都用2位国家简称
        tradeParam.put("billState", getProvince(trade.getCoreCardholder().getCountry(), trade.getCoreCardholder().getProvince(), trade.getBillProvinceConstant()));//US CA必填，其他否。按照附录对应的两位简称传入  ///////////
        tradeParam.put("billCity", trade.getCoreCardholder().getCity());
        tradeParam.put("billEmail", trade.getCoreCardholder().getEmail());
        tradeParam.put("billPhoneNumber", trade.getCoreCardholder().getPhone());
        //收货信息
        tradeParam.put("shippingName", trade.getCoreOrderDetail().getFirstName() + " " + trade.getCoreOrderDetail().getLastName());
        tradeParam.put("shippingAddress", trade.getCoreOrderDetail().getAddress());
        tradeParam.put("shippingPostalCode", trade.getCoreOrderDetail().getZip());
        tradeParam.put("shippingCountry", trade.getCoreOrderDetail().getCountry().toUpperCase());
        tradeParam.put("shippingState", getProvince(trade.getCoreOrderDetail().getCountry(), trade.getCoreOrderDetail().getProvince(), trade.getShipProvinceConstant()));
        tradeParam.put("shippingCity", trade.getCoreOrderDetail().getCity());
        tradeParam.put("shippingEmail", trade.getCoreOrderDetail().getEmail());
        tradeParam.put("shippingPhoneNumber", trade.getCoreOrderDetail().getPhone());
        //风控信息
        tradeParam.put("deviceFingerprintID", "m" + param.getMerNum() + "_2017");
        tradeParam.put("registerUserEmail", trade.getCoreCardholder().getEmail());//商户注册邮箱
        tradeParam.put("registerTime", dateFormat.format(trade.getCoreOrder().getTradeTime()));//注册时间
        tradeParam.put("registerIp", "127.0.0.1");//注册Ip
        tradeParam.put("registerTerminal", "00");//注册终端设备 00 PC 01 手机 02 其他
        tradeParam.put("orderIp", trade.getCoreOrderDetail().getIp());
        tradeParam.put("orderTerminal", "00");//下单时终端设备
        tradeParam.put("referer", trade.getCoreOrderDetail().getReferer());

        String md5Txt = "version=1.6" +
                "&merchantId=" + param.getMerNum() +
                "&signType=MD5" +
                "&merchantOrderNo=" + trade.getCoreOrder().getId() +
                "&currencyCode=" + "CNY" +
                "&orderAmount=" + orderAmount +
                "&submitTime=" + dateFormat.format(trade.getCoreOrder().getTradeTime()) +
                "&cardNumber=" + trade.getTradeObject().getCardNo() +
                "&cardExpirationMonth=" + cardExpirationMonth +
                "&cardExpirationYear=" + trade.getCoreCardDetail().getExpYear() +
                "&securityCode=" + trade.getCoreCardDetail().getRsa() +
                "&key=" + param.getMd5key();

        tradeParam.put("signMsg", DigestUtils.md5Hex(md5Txt));
        return tradeParam;
    }

    // GrePay交易返回校验
    private BankResult verifyResult(String content) {
        logger.info("GrePay返回结果：" + content);
        BankResult bankResult = new BankResult();
        bankResult.setParam(content);
        GrePayBean result = JsonUtils.jsonToPojo(content, GrePayBean.class);
        if (result != null) {
            String returnMd5Txt = "signType=MD5" +
                    "&merchantOrderNo=" + StringUtils.getToStringInsteadOfNull(result.getMerchantOrderNo()) +
                    "&currencyCode=" + StringUtils.getToStringInsteadOfNull(result.getCurrencyCode()) +
                    "&orderAmount=" + StringUtils.getToStringInsteadOfNull(result.getOrderAmount()) +
                    "&paidAmount=" + StringUtils.getToStringInsteadOfNull(result.getPaidAmount()) +
                    "&resultCode=" + StringUtils.getToStringInsteadOfNull(result.getResultCode()) +
                    "&key=" + param.getMd5key();
            bankResult.setBankOrderId(result.getMasapayOrderNo());
            Date date = DateUtils.dateFormat(dateFormat, result.getDealTime());
            bankResult.setTime(date == null ? new Date() : date);
            if (StringUtils.equals(result.getSignMsg(), DigestUtils.md5Hex(returnMd5Txt).toUpperCase())) {
                switch (result.getResultCode()) {
                    case SUCCESS:
                        bankResult.setStatus(SystemConstant.BANK_STATUS_SUCCESS);
                        bankResult.setBankResult("交易成功");
                        bankResult.setBankCode("pay.1047");
                        break;
                    case ACCEPTED:
                        bankResult.setStatus(SystemConstant.BANK_STATUS_PENDING_PAYMENT);
                        bankResult.setBankResult("订单已受理");
                        bankResult.setBankCode("pay.1043");
                        break;
                    case FAIL:
                        bankResult.setStatus(SystemConstant.BANK_STATUS_FAIL);
                        bankResult.setBankResult("付款失败：" + result.getErrMsg());
                        bankResult.setBankCode("pay.1048");
                        String bankCode;
                        try {
                            bankCode = BankResultInfoCode.CommunicateCodeTwo.valueOf("gr" + result.getErrCode()).getStatusMsg();
                        } catch (Exception e) {
                            logger.error("GrePay未定义的错误信息,错误编码为：" + result.getErrCode());
                            bankCode = "error.5000";
                        }
                        bankResult.setBankCode(bankCode);
                        break;
                    default:
                        break;
                }
            } else {
                bankResult.setBankResult("MD5验证失败！");
                bankResult.setStatus(SystemConstant.BANK_STATUS_FAIL);
                bankResult.setBankCode("error.5000");
                logger.error("MD5验证失败：" + returnMd5Txt);
            }
            return bankResult;
        } else {
            String msg = "交易申请：银行返回Json转换异常！";
            logger.error(msg);
            bankResult.setStatus(SystemConstant.BANK_STATUS_FAIL);
            bankResult.setBankResult(msg);
            return bankResult;
        }
    }

    private String getProvince(String country, String province, SysConstant provinceConstant) {
        if (StringUtils.equalsAnyIgnoreCase(country, "US", "CA")) {
            return provinceConstant.getSecondValue();
        } else {
            return province;
        }
    }
}